package open.kiwipeach.autoconfigure.web.utils;

import cn.kiwipeach.blog.domain.AccessTokenVO;
import cn.kiwipeach.blog.exception.BlogException;
import com.alibaba.fastjson.JSON;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import open.kiwipeach.autoconfigure.shiro.token.AccessToken;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 描述：token加密解密工具类
 *
 * @author kiwipeach
 * @since 2.0
 */
@Slf4j
public class JwtTokenUtil {
    /**
     * jwt签发者
     */
    private static final String ISSUER_EMAIL = "kiwipeach@qq.com";
    /**
     * jwt所面向的用户
     */
    private static final String SUBJECT = "everyone";
    /**
     * 定义在什么时间之前，该jwt都是不可用的.
     */
    private static final long NOT_BEFORE = 0;
    /**
     * //TODO 秘钥和超时时间可配置化处理
     * 秘钥
     */
    private static final String SECRET = "secret";
    /**
     * jwt的过期时间，这个过期时间必须要大于签发时间（10分钟）
     */
    private static final long EXPIRE = 60 * 1000 * 60;

    /**
     * jwt的过期时间，这个过期时间必须要大于签发时间（10分钟）
     */
    private static final String USER_TOKEN = "User-Token";

    /**
     * 签发token字符串
     *
     * @param accessToken 访问对象
     * @return 返回访问对象
     */
    public static AccessTokenVO signToken(AccessToken accessToken) {
        try {
            Date nowDate = new Date();
            String formatTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date(nowDate.getTime() + EXPIRE));
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            Map<String, Object> claims = new HashMap<String, Object>();
            String refreshToken = UUID.randomUUID().toString().replace("-", "");
            claims.put("userId", accessToken.getId());
            claims.put("userName", accessToken.getUserName());
            claims.put("nickName", accessToken.getNickName());
            claims.put("headUrl", accessToken.getHeadUrl());
            claims.put("expiresTime", formatTime);
            claims.put("refreshToken", refreshToken);
            String token = JWT.create()
                    .withIssuer(ISSUER_EMAIL)
                    .withSubject(SUBJECT)
                    .withIssuedAt(nowDate)
                    .withExpiresAt(new Date(nowDate.getTime() + EXPIRE))
                    .withNotBefore(new Date(nowDate.getTime() + NOT_BEFORE))
                    .withJWTId(UUID.randomUUID().toString())
                    .withClaim(USER_TOKEN, claims)
                    .sign(algorithm);
            accessToken.setToken(token);
            accessToken.setExpiresTime(formatTime);
            accessToken.setRefreshToken(refreshToken);
        } catch (JWTCreationException exception) {
            log.error("token签发出错:{}", exception.getMessage());
        }
        log.warn("签发token对象:{}", accessToken);
        return new AccessTokenVO(accessToken.getToken(), accessToken.getExpiresTime(), accessToken.getRefreshToken());
    }

    /**
     * 验证token字符串
     *
     * @param token 当前登录的用户Token对象
     * @return 返回验证结果
     */
    public static boolean verify(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer(ISSUER_EMAIL)
                    .withSubject(SUBJECT)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            Map<String, Object> user = jwt.getClaim(USER_TOKEN).asMap();
            log.info("验证token结果:{}", user);
            return true;
        } catch (Exception e) {
            //Invalid signature/claims
            log.info("token验证失败:{}", e.getMessage());
            throw new BlogException("-LOGIN-002", "当前用户token失效或者非法");
        }
    }

    /**
     * 解密token密文
     *
     * @param token token字符串
     * @return 返回访问token
     */
    public static AccessToken decode(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            Map<String, Object> user = jwt.getClaim(USER_TOKEN).asMap();
            String userId = (String) user.get("userId");
            String userName = (String) user.get("userName");
            String nickName = (String) user.get("nickName");
            String headUrl = (String) user.get("headUrl");
            String refreshToken = (String) user.get("refreshToken");
            log.info("获取解密:{}", user);
            AccessToken accessToken = new AccessToken();
            accessToken.setUserName(userName);
            accessToken.setId(userId);
            accessToken.setNickName(nickName);
            accessToken.setHeadUrl(headUrl);
            accessToken.setToken(token);
            String format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(jwt.getExpiresAt());
            accessToken.setExpiresTime(format);
            accessToken.setRefreshToken(refreshToken);
            return accessToken;
        } catch (JWTDecodeException exception) {
            //Invalid token
            log.info("token解密失败:{}", exception.getMessage());
            throw new BlogException("-LOGIN-002", "用户token信息解码失败");
        }
    }

}
